use bytes::BytesMut;
use chrono::{DateTime, Local};
use tokio_postgres::types::*;

#[derive(Debug, Default)]
pub struct Blog {
    pub id: u32,
    pub title: String,
    pub content: String,
    pub tags: Vec<String>,
    pub author: String,
    pub author_id: String,
    pub views: u32,
    pub stars: u32,
    pub created_at: DateTime<Local>,
}

#[derive(Debug, Default)]
pub struct AddRequest {
    pub title: String,
    pub content: String,
    pub tags: Vec<String>,
    pub author: String,
    pub author_id: String,
}

#[derive(Debug, Default)]
pub struct UpdateRequest {
    pub id: u32,
    pub title: String,
    pub content: String,
    pub tags: Vec<String>,
}

#[derive(Debug, Default)]
pub struct GetMutipleRequest {
    // Last id of previous page, or 0 if getting first page
    pub id: u32,

    // Page capacity
    pub count: u32,
}

#[derive(Debug, Default)]
pub struct GetMutipleResponse {
    pub total_count: u32,
    pub blog_count: u32,
    pub blogs: Vec<Blog>,
}

impl FromSql<'_> for Blog {
    fn from_sql(
        ty: &Type,
        raw: &[u8],
    ) -> Result<Self, Box<(dyn std::error::Error + Send + Sync + 'static)>> {
        // 解析原始字节流为 PostgreSQL 的类型
        let id: u32 = FromSql::from_sql(ty, raw)?;
        let title: String = FromSql::from_sql(ty, raw)?;
        let content: String = FromSql::from_sql(ty, raw)?;
        let tags: Vec<String> = FromSql::from_sql(ty, raw)?;
        let author: String = FromSql::from_sql(ty, raw)?;
        let author_id: String = FromSql::from_sql(ty, raw)?;
        let views: u32 = FromSql::from_sql(ty, raw)?;
        let stars: u32 = FromSql::from_sql(ty, raw)?;
        let created_at: DateTime<Local> = FromSql::from_sql(ty, raw)?;

        Ok(Blog {
            id,
            title,
            content,
            tags,
            author,
            author_id,
            views,
            stars,
            created_at,
        })
    }

    fn accepts(ty: &Type) -> bool {
        matches!(ty, &Type::RECORD)
    }
}

impl ToSql for Blog {
    fn to_sql(
        &self,
        ty: &Type,
        out: &mut bytes::BytesMut,
    ) -> Result<IsNull, Box<(dyn std::error::Error + Send + Sync + 'static)>> {
        // 检查提供的类型是否是我们期望的类型
        if !matches!(ty, &Type::RECORD) {
            return Err("Type mismatch".into());
        }

        // 将每个字段转换为 PostgreSQL 可以理解的二进制格式
        self.id.to_sql(&Type::OID, out)?;
        self.title.to_sql(&Type::VARCHAR, out)?;
        self.content.to_sql(&Type::TEXT, out)?;
        self.tags.to_sql(&Type::VARCHAR_ARRAY, out)?;
        self.author.to_sql(&Type::VARCHAR, out)?;
        self.author_id.to_sql(&Type::VARCHAR, out)?;
        self.views.to_sql(&Type::OID, out)?;
        self.stars.to_sql(&Type::OID, out)?;
        self.created_at.to_sql(&Type::TIMESTAMP, out)?;

        Ok(IsNull::No)
    }

    fn accepts(ty: &Type) -> bool {
        matches!(ty, &Type::RECORD)
    }

    fn to_sql_checked(
        &self,
        ty: &Type,
        out: &mut bytes::BytesMut,
    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
        Ok(self.to_sql(ty, out)?)
    }
}

impl ToSql for AddRequest {
    fn to_sql(
        &self,
        ty: &Type,
        out: &mut bytes::BytesMut,
    ) -> Result<IsNull, Box<(dyn std::error::Error + Send + Sync + 'static)>> {
        // 检查提供的类型是否是我们期望的类型
        if !matches!(ty, &Type::RECORD) {
            return Err("Type mismatch".into());
        }

        // 将每个字段转换为 PostgreSQL 可以理解的二进制格式
        self.title.to_sql(&Type::VARCHAR, out)?;
        self.content.to_sql(&Type::TEXT, out)?;
        self.tags.to_sql(&Type::VARCHAR_ARRAY, out)?;
        self.author.to_sql(&Type::VARCHAR, out)?;
        self.author_id.to_sql(&Type::VARCHAR, out)?;

        Ok(IsNull::No)
    }

    fn accepts(ty: &Type) -> bool {
        matches!(ty, &Type::RECORD)
    }

    fn to_sql_checked(
        &self,
        ty: &Type,
        out: &mut bytes::BytesMut,
    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
        Ok(self.to_sql(ty, out)?)
    }
}
